package com.yzt.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Y <br>
 * @date 2020年11月26日 <br>
 *       <br>
 */
@Configuration
public class ShiroConfig {

	@Value("${spring.redis.host}")
	public String redis_host;

	@Value("${spring.redis.port}")
	public int redis_port;

	@Value("${spring.redis.password}")
	public String redis_password;

	@Value("${spring.redis.timeout}")
	public int redis_timeout;

	@Value("${spring.redis.expire}")
	public int redis_expire;

	@Value("${spring.redis.shiro_database}")
	public int redis_database;

	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		System.out.println("ShiroConfiguration.shirFilter()");

		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 注意过滤器配置顺序 不能颠倒
		// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了，登出后跳转配置的loginUrl
		// 配置不会被拦截的链接 顺序判断
		filterChainDefinitionMap.put("/**", "anon");

		filterChainDefinitionMap.put("/css/**", "anon");
		filterChainDefinitionMap.put("/js/**", "anon");
		filterChainDefinitionMap.put("/imgs/**", "anon");
		filterChainDefinitionMap.put("/imgs/**", "anon");

//        filterChainDefinitionMap.put("/logout", "logout");

		filterChainDefinitionMap.put("/user/info/list", "authc");
//		filterChainDefinitionMap.put("/**", "authc");

		// 配置shiro默认登录界面地址，前后端分离中登录界面跳转应由前端路由控制，后台仅返回json数据
		shiroFilterFactoryBean.setLoginUrl("/login");

		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/home");
		// 未授权界面;
		shiroFilterFactoryBean.setUnauthorizedUrl("/403");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * 凭证匹配器 （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 ）
	 *
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);// 散列算法:这里使用更安全的sha256算法;
		hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false); // 数据库存储的密码字段使用HEX还是BASE64方式加密
		hashedCredentialsMatcher.setHashIterations(10);// 散列的次数
		return hashedCredentialsMatcher;
	}

	/**
	 * 自定义Shiro认证、授权类
	 *
	 * @return
	 */
	@Bean
	public ShiroRealm shiroRealm() {
		ShiroRealm myShiroRealm = new ShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * shiro-redis缓存管理实例实现
	 *
	 * @return
	 */
	@Bean
	public RedisManager shrioRedisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(redis_host + ":" + redis_port);
		redisManager.setPassword(redis_password);
		redisManager.setTimeout(redis_timeout);
		redisManager.setDatabase(redis_database);
		return redisManager;
	}

	/**
	 * Shiro依赖Redis的普通缓存管理实例
	 *
	 * @return
	 */
	@Bean("shrioRedisCacheManager")
	public RedisCacheManager shrioRedisCacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setExpire(redis_expire);
		redisCacheManager.setRedisManager(shrioRedisManager());
		return redisCacheManager;
	}

	/**
	 * Shiro依赖Redis的Session缓存管理实例
	 *
	 * @return
	 */
	@Bean
	public DefaultWebSessionManager shiroRedisSessionManager() {
		DefaultWebSessionManager defaultSessionManager = new DefaultWebSessionManager();
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setExpire(redis_expire);
		redisSessionDAO.setRedisManager(shrioRedisManager());
		defaultSessionManager.setSessionDAO(redisSessionDAO);
		return defaultSessionManager;
	}

	/**
	 * Shiro的Cookie实例创建
	 *
	 * @return
	 */
	@Bean
	public SimpleCookie rememberMeCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(7 * 24 * 60 * 60);
		return simpleCookie;
	}

	/**
	 * Shiro自定义Cookie记住我实例
	 *
	 * @return
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager() {
		// System.out.println("ShiroConfiguration.rememberMeManager()");
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		// rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
		cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
		return cookieRememberMeManager;
	}

	@Bean
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroRealm());
		// 自定义缓存实现 使用redis
		securityManager.setCacheManager(shrioRedisCacheManager());
		securityManager.setSessionManager(shiroRedisSessionManager());
		// 实现记住我
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * 开启shiro aop注解支持,使用代理方式 需要开启代码支持
	 *
	 * @return
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}
}
